package info.motteke.mixivoice4j;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

public class MixiVoiceObserver {

    private final List<EntryUpdateListener> entryUpdateListeners = newList();

    private final List<VoiceCreateListener> voiceCreateListeners = newList();

    private final List<UserUpdateListener> userUpdateListeners = newList();

    private final List<FavoriteUpdateListener> favoriteUpdateListeners = newList();

    private final Set<Entry> updatedEntries = newSet();

    private final Set<Voice> newVoices = newSet();

    private final Set<User> updatedUsers = newSet();

    private final Set<Entry> favoriteUpdatedEntries = newSet();

    private final AtomicInteger eventSize = new AtomicInteger(0);

    public MixiVoiceObserver() {
        /* empty */
    }

    public void addEntryUpdateListener(EntryUpdateListener listener) {
        entryUpdateListeners.add(listener);
    }

    public void addVoiceCreateListener(VoiceCreateListener listener) {
        voiceCreateListeners.add(listener);
    }

    public void addUserUpdateListener(UserUpdateListener listener) {
        userUpdateListeners.add(listener);
    }

    public void addFavoriteUpdateListener(FavoriteUpdateListener listener) {
        favoriteUpdateListeners.add(listener);
    }

    /**
     * 情報が更新されたユーザを登録します。<br>
     * 以下のユーザが該当します。
     *
     * <ul>
     * <li>新しく作成された。
     * <li>ニックネームが変更された。
     * <li>サムネイルが変更された。
     * </ul>
     *
     * @param user
     *            ユーザ
     */
    public void addUpdatedUser(User user) {
        updatedUsers.add(user);
    }

    /**
     * 更新されたエントリを登録します。<br>
     * 以下のエントリが該当します。
     *
     * <ul>
     * <li>新しく作成された。
     * <li>コメントが追加された。
     * <li>イイネ！が追加された。
     * </ul>
     *
     * @param entry
     *            更新されたエントリ
     */
    public void addUpdatedEntry(Entry entry) {
        updatedEntries.add(entry);
    }

    /**
     * 新しく作成されたボイスを登録します。
     *
     * @param voice
     *            新しく作成されたボイス
     */
    public void addNewVoice(Voice voice) {
        newVoices.add(voice);
    }

    /**
     * イイネ！が更新されたボイスを登録します。
     *
     * @param entry
     *            イイネ！が更新されたボイス
     */
    public void addFavoriteUpdatedEntry(Entry entry) {
        favoriteUpdatedEntries.add(entry);
    }

    /**
     * 監視を開始します。
     */
    public void start() {
        eventSize.incrementAndGet();
    }

    /**
     * 監視を終了します。
     */
    public void end() {
        if (eventSize.decrementAndGet() != 0) {
            return;
        }

        if (updatedEntries.size() != 0) {
            notifyUpdatedEntries();
        }

        if (newVoices.size() != 0) {
            notifyCreatedVoices();
        }

        if (updatedUsers.size() != 0) {
            notifyUpdatedUsers();
        }

        if (favoriteUpdatedEntries.size() != 0) {
            notifyFavoriteUpdatedEntries();
        }

        updatedEntries.clear();
        newVoices.clear();
        updatedUsers.clear();
        favoriteUpdatedEntries.clear();
    }

    /**
     * 更新されたユーザをリスナに通知します。
     */
    private void notifyUpdatedUsers() {
        for (UserUpdateListener listener : userUpdateListeners) {
            listener.onUserUpdate(updatedUsers);
        }
    }

    /**
     * イイネ！が更新されたエントリをリスナに通知します。
     */
    private void notifyFavoriteUpdatedEntries() {
        for (FavoriteUpdateListener listener : favoriteUpdateListeners) {
            listener.onFavoriteUpdate(favoriteUpdatedEntries);
        }
    }

    /**
     * 更新されたエントリをリスナに通知します。
     */
    private void notifyUpdatedEntries() {
        for (EntryUpdateListener listener : entryUpdateListeners) {
            listener.onEntryUpdate(updatedEntries);
        }
    }

    /**
     * 更新されたボイスをリスナに通知します。
     */
    private void notifyCreatedVoices() {
        for (VoiceCreateListener listener : voiceCreateListeners) {
            listener.onVoiceCreated(newVoices);
        }
    }

    /**
     * 新しいリストを作成します。
     *
     * @param <T>
     *            要素の型
     * @return 新しく作成されたリスト
     */
    private <T> List<T> newList() {
        return new ArrayList<T>();
    }

    /**
     * 新しいセットを作成します。
     *
     * @param <T>
     *            要素の型
     * @return 新しく作成されたセット
     */
    private <T> Set<T> newSet() {
        return new LinkedHashSet<T>();
    }
}
